let Awesome = (() => {
  let manifest = chrome.runtime.getManifest();
  const SERVER_SITE = manifest.homepage_url,
    URL_TOOL_TPL = `${SERVER_SITE}/#TOOL-NAME#/index.html`,
    TOOL_NAME_TPL = "DYNAMIC_TOOL:#TOOL-NAME#",
    TOOL_CONTENT_SCRIPT_TPL = "DYNAMIC_TOOL:CS:#TOOL-NAME#",
    TOOL_CONTENT_SCRIPT_CSS_TPL = "DYNAMIC_TOOL:CS:CSS:#TOOL-NAME#",
    TOOL_MENU_TPL = "DYNAMIC_MENU:#TOOL-NAME#";
  let StorageMgr = {
      get: (e) =>
        new Promise((t, o) => {
          chrome.storage.local.get(e, (o) => {
            t("string" == typeof e ? o[e] : o);
          });
        }),
      set: (e, t) =>
        new Promise((o, n) => {
          if ("string" == typeof e) {
            let o = {};
            (o[e] = t), (e = o);
          }
          chrome.storage.local.set(e, () => {
            o();
          });
        }),
      remove: (e) =>
        new Promise((t, o) => {
          (e = [].concat(e)),
            chrome.storage.local.remove(e, () => {
              t();
            });
        }),
    },
    detectInstall = (e, t, o) => {
      let n = TOOL_MENU_TPL.replace("#TOOL-NAME#", e),
        r = TOOL_NAME_TPL.replace("#TOOL-NAME#", e);
      return "json-format" !== e || o
        ? Promise.all([StorageMgr.get(r), StorageMgr.get(n)]).then((e) =>
            t ? e[0] && "1" === String(e[1]) : o ? e[0] : !!e[0]
          )
        : t
        ? StorageMgr.get(n).then((e) => "0" !== String(e))
        : Promise.resolve(!0);
    },
    log = (e) => {},
    _tplHandler = (e, t) => {
      let o = {};
      return (
        [
          /<link\s+[^>]*stylesheet[^>]*href=['"]([^'"]+)['"][^>]*>/gim,
          /<script[^>]*src=['"]([^'"]+)['"][^>]*>\s*?<\/[^>]*script>/gim,
        ].forEach((n) => {
          t = t.replace(n, (t, n) => {
            let r = n,
              l = n;
            if (-1 !== n.indexOf("?")) {
              let e = n.split("?");
              e.pop(), (n = e.join(""));
            }
            if (
              (/^\./.test(n) || ((n = `../${e}/${n}`), (l = `../${e}/${l}`)),
              "../static/js/navbar.js" !== n)
            ) {
              let a = `${SERVER_SITE}/${e}/${r}`,
                s = /<script/.test(t) ? "js" : "css";
              (o[s] = o[s] || []), o[s].push([n, l, a]);
            }
            return "";
          });
        }),
        {
          html: (t = t
            .replace("static/img/favicon.ico", "static/img/fe-16.png")
            .replace(/<body/gm, "<body browser-extension")
            .replace(
              /<div\s+class="mod-pageheader">.*<\/div>\s*<div\sclass="panel\spanel-default"/gm,
              '<div class="panel panel-default"'
            )
            .replace(
              /<div\sid="pageFooter"\s+[^>]+>\s*<script\s.*<\/script>\s*<\/div>/gm,
              ""
            )
            .replace(
              /<\/body>/,
              () =>
                Object.keys(o)
                  .map(
                    (e) =>
                      `<dynamic data-type="${e}" data-source="${o[e]
                        .map((e) => e[1])
                        .join(",")}"></dynamic>`
                  )
                  .join("") + "</body>"
            )),
          jsCss: o,
        }
      );
    },
    install = (toolName, fnProgress) => (
      log(toolName + "工具开始安装/更新..."),
      new Promise((resolve, reject) => {
        fetch(URL_TOOL_TPL.replace("#TOOL-NAME#", toolName))
          .then((e) => {
            if (e.ok) return fnProgress && fnProgress("1%"), e.text();
            reject && reject({ status: e.status, statusText: e.statusText });
          })
          .then((html) => {
            let result = _tplHandler(toolName, html);
            html = result.html;
            let files = result.jsCss,
              total =
                eval(
                  Object.values(files)
                    .map((e) => e.length)
                    .join("+")
                ) + 1,
              loaded = 1;
            fnProgress && fnProgress(Math.floor((100 * loaded) / total) + "%"),
              (async () => {
                let e = getAllTools(!0);
                for (let t in files)
                  for (let o = 0; o < files[t].length; o++) {
                    let n = files[t][o];
                    await fetch(n[2])
                      .then((e) => e.text())
                      .then((t) => {
                        StorageMgr.set(n[0], t),
                          e[toolName].contentScript &&
                            -1 !==
                              n[0].indexOf(toolName + "/content-script.js") &&
                            (StorageMgr.set(
                              TOOL_CONTENT_SCRIPT_TPL.replace(
                                "#TOOL-NAME#",
                                toolName
                              ),
                              t
                            ),
                            e[toolName].contentScriptCss &&
                              fetch(
                                n[2].replace(
                                  "content-script.js",
                                  "content-script.css"
                                )
                              )
                                .then((e) => e.text())
                                .then((e) => {
                                  StorageMgr.set(
                                    TOOL_CONTENT_SCRIPT_CSS_TPL.replace(
                                      "#TOOL-NAME#",
                                      toolName
                                    ),
                                    e
                                  );
                                })),
                          log(`${toolName}工具静态文件${n[0]}安装/更新成功！`),
                          fnProgress &&
                            fnProgress(
                              Math.floor((100 * ++loaded) / total) + "%"
                            );
                      });
                  }
                log(toolName + "工具安装/更新成功！"),
                  resolve && resolve("100%");
              })(),
              StorageMgr.set(
                TOOL_NAME_TPL.replace("#TOOL-NAME#", toolName),
                html || "&nbsp;"
              ),
              log(toolName + "工具html模板安装/更新成功！");
          })
          .catch((e) => reject && reject(e));
      })
    ),
    offLoad = (e) => {
      let t = [];
      t.push(TOOL_NAME_TPL.replace("#TOOL-NAME#", e)),
        t.push(TOOL_CONTENT_SCRIPT_TPL.replace("#TOOL-NAME#", e)),
        t.push(TOOL_CONTENT_SCRIPT_CSS_TPL.replace("#TOOL-NAME#", e));
      for (let o in localStorage) String(o).startsWith(`../${e}/`) && t.push(o);
      return (
        chrome.storage.local.get(null, (t) => {
          StorageMgr.remove(
            Object.keys(t).filter((t) => String(t).startsWith(`../${e}/`))
          );
        }),
        log(e + " 卸载成功！"),
        t.forEach((e) => localStorage.removeItem(e)),
        StorageMgr.remove(t)
      );
    },
    makeStorageUnlimited = () => {
      for (let e in localStorage)
        (/^DYNAMIC_/.test(e) || /^\.\.\//.test(e)) &&
          chrome.storage.local.get(e, (t) => {
            if (!t.hasOwnProperty(e)) {
              let t = {};
              (t[e] = localStorage.getItem(e)),
                chrome.storage.local.set(t, () => {
                  localStorage.removeItem(e);
                }),
                console.log("数据迁移完成：", e);
            }
          });
    },
    gcLocalFiles = () =>
      getAllTools().then((e) =>
        Object.keys(e).forEach((t) => {
          e[t]._devTool || e[t].installed || offLoad(t);
        })
      ),
    getInstalledTools = () =>
      getAllTools().then((e) => {
        let t = {};
        return (
          Object.keys(e).filter((o) => {
            e[o].installed && (t[o] = e[o]);
          }),
          t
        );
      }),
    getContentScript = (e, t) =>
      StorageMgr.get(
        t
          ? TOOL_CONTENT_SCRIPT_CSS_TPL.replace("#TOOL-NAME#", e)
          : TOOL_CONTENT_SCRIPT_TPL.replace("#TOOL-NAME#", e)
      ),
    getToolTpl = (e) => StorageMgr.get(TOOL_NAME_TPL.replace("#TOOL-NAME#", e)),
    checkUpgrade = (e) => {
      return Promise.all([
        ((e) =>
          fetch(URL_TOOL_TPL.replace("#TOOL-NAME#", e)).then((e) => e.text()))(
          e
        ),
        ((e) => StorageMgr.get(TOOL_NAME_TPL.replace("#TOOL-NAME#", e)))(e),
      ]).then((t) => {
        let o = _tplHandler(e, t[0]);
        return t[1] !== o.html;
      });
    },
    menuMgr = (e, t) => {
      let o = TOOL_MENU_TPL.replace("#TOOL-NAME#", e);
      switch (t) {
        case "get":
          return StorageMgr.get(o);
        case "offload":
          return log(e + " 卸载成功！"), StorageMgr.set(o, 0);
        case "install":
          return log(e + " 安装成功！"), StorageMgr.set(o, 1);
      }
    },
    CodeCacheMgr = {
      get: () => localStorage.getItem("TOOLS_FROM_REMOTE"),
      set: (e) => {
        localStorage.setItem("TOOLS_FROM_REMOTE", e);
      },
    },
    SortToolMgr = {
      get: () =>
        []
          .concat(JSON.parse(localStorage.getItem("TOOLS_CUSTOM_SORT") || "[]"))
          .filter((e) => !!e),
      set: (e) => {
        localStorage.setItem(
          "TOOLS_CUSTOM_SORT",
          JSON.stringify([].concat(e || []).filter((e) => !!e))
        );
      },
    },
    getAllTools = (declareOnly) => {
      let toolMap = {
        "json-format": {
          name: "JSON美化工具",
          tips: "页面自动检测并格式化、手动格式化、乱码解码、排序、BigInt、编辑、下载、皮肤定制等",
          contentScript: !0,
          contentScriptCss: !0,
          offloadForbid: !0,
          menuConfig: [
            {
              icon: "⒥",
              text: "JSON格式化",
              contexts: ["page", "selection", "editable"],
            },
          ],
        },
        "json-diff": {
          name: "JSON比对工具",
          tips: "支持两个JSON内容的自动键值比较，并高亮显示差异点，同时也能判断JSON是否合法",
          menuConfig: [{ icon: "☷", text: "JSON比对器" }],
        },
        "qr-code": {
          name: "二维码/解码",
          tips: "支持自定义颜色和icon的二维码生成器，并且支持多种模式的二维码解码，包括截图后粘贴解码",
          contentScript: !0,
          menuConfig: [
            {
              icon: "▣",
              text: "二维码生成器",
              contexts: ["page", "selection", "editable", "link", "image"],
            },
            { icon: "◈", text: "二维码解码器", contexts: ["image"] },
          ],
        },
        "image-base64": {
          name: "图片转Base64",
          tips: "支持多种模式的图片转Base64格式，比如链接粘贴/截图粘贴等，也支持Base64数据逆转图片",
          menuConfig: [
            { icon: "▤", text: "图片与base64", contexts: ["image"] },
          ],
        },
        "sticky-notes": {
          name: "我的便签笔记",
          tips: "方便快捷的浏览器便签笔记工具，支持创建目录对笔记进行分类管理，笔记支持一键导出/导入",
          menuConfig: [{ icon: "✐", text: "我的便签笔记" }],
        },
        "en-decode": {
          name: "信息编码转换",
          tips: "支持多格式的信息编解码，如Unicode、UTF-8、UTF-16、URL、Base64、MD5、Hex、Gzip等",
          menuConfig: [
            {
              icon: "♨",
              text: "字符串编解码",
              contexts: ["page", "selection", "editable"],
            },
          ],
        },
        "code-beautify": {
          name: "代码美化工具",
          tips: "支持多语言的代码美化，包括 Javascript、CSS、HTML、XML、SQL，且会陆续支持更多格式",
          menuConfig: [
            {
              icon: "✡",
              text: "代码美化工具",
              contexts: ["page", "selection", "editable"],
            },
          ],
        },
        timestamp: {
          name: "时间(戳)转换",
          tips: "本地化时间与时间戳之间的相互转换，支持秒/毫秒、支持世界时区切换、各时区时钟展示等",
          menuConfig: [{ icon: "♖", text: "时间(戳)转换" }],
        },
        password: {
          name: "随机密码生成",
          tips: "将各种字符进行随机组合生成密码，可以由数字、大小写字母、特殊符号组成，支持指定长度",
          menuConfig: [{ icon: "♆", text: "随机密码生成" }],
        },
        html2markdown: {
          name: "Markdown转换",
          tips: "Markdown编写/预览工具，支持HTML片段直接转Markdown，支持将内容以PDF格式进行下载",
          menuConfig: [{ icon: "ⓜ", text: "markown工具" }],
        },
        postman: {
          name: "简易版Postman",
          tips: "开发过程中的接口调试工具，支持GET/POST/HEAD请求方式，且支持JSON内容自动格式化",
          menuConfig: [{ icon: "☯", text: "简易Postman" }],
        },
        regexp: {
          name: "JS正则表达式",
          tips: "正则校验工具，默认提供一些工作中常用的正则表达式，支持内容实时匹配并高亮显示结果",
          menuConfig: [{ icon: "✙", text: "JS正则表达式" }],
        },
        "trans-radix": {
          name: "进制转换工具",
          tips: "支持2进制到36进制数据之间的任意转换，比如：10进制转2进制，8进制转16进制，等等",
          menuConfig: [{ icon: "❖", text: "进制转换工具" }],
        },
        "trans-color": {
          name: "颜色转换工具",
          tips: "支持HEX颜色到RGB格式的互转，比如HEX颜色「#43ad7f」转RGB后为「rgb(67, 173, 127)」",
          menuConfig: [{ icon: "▶", text: "颜色转换工具" }],
        },
        crontab: {
          name: "Crontab工具",
          tips: "一个简易的Crontab生成工具，支持随机生成Demo，编辑过程中，分时日月周会高亮提示",
          menuConfig: [{ icon: "½", text: "Crontab生成" }],
        },
        "loan-rate": {
          name: "贷(还)款利率",
          tips: "贷款或还款利率的计算器，按月呈现还款计划；并支持按还款额反推贷款实际利率",
          menuConfig: [{ icon: "$", text: "贷(还)款利率" }],
        },
        devtools: {
          name: "FH开发者工具",
          tips: "以开发平台的思想，FeHelper支持用户进行本地开发，将自己的插件功能集成进FH工具市场",
          menuConfig: [{ icon: "㉿", text: "FH开发者工具" }],
        },
      };
      Object.keys(toolMap).forEach((e) => {
        switch (
          ((toolMap[e].installed = toolMap[e].offloadForbid),
          (toolMap[e].menu = toolMap[e].offloadForbid),
          (toolMap[e].upgrade = !1),
          e)
        ) {
          case "json-format":
            toolMap[e].menuConfig[0].onClick = function (e, t) {
              chrome.tabs.executeScript(
                t.id,
                {
                  code:
                    "(" +
                    function (e) {
                      return e.selectionText;
                    }.toString() +
                    ")(" +
                    JSON.stringify(e) +
                    ")",
                  allFrames: !1,
                },
                function (e) {
                  chrome.DynamicToolRunner({
                    tool: "json-format",
                    withContent: e[0],
                  });
                }
              );
            };
            break;
          case "code-beautify":
          case "en-decode":
            toolMap[e].menuConfig[0].onClick = function (t, o) {
              chrome.tabs.executeScript(
                o.id,
                {
                  code:
                    "(" +
                    function (e) {
                      let t = e.linkUrl,
                        o = e.pageUrl,
                        n = e.srcUrl,
                        r = e.selectionText;
                      return t || n || r || o;
                    }.toString() +
                    ")(" +
                    JSON.stringify(t) +
                    ")",
                  allFrames: !1,
                },
                function (t) {
                  chrome.DynamicToolRunner({
                    withContent: t[0],
                    query: `tool=${e}`,
                  });
                }
              );
            };
            break;
          case "qr-code":
            (toolMap[e].menuConfig[0].onClick = function (e, t) {
              chrome.tabs.executeScript(
                t.id,
                {
                  code:
                    "(" +
                    function (e) {
                      let t = e.linkUrl,
                        o = e.pageUrl,
                        n = e.srcUrl,
                        r = e.selectionText;
                      return t || n || r || o;
                    }.toString() +
                    ")(" +
                    JSON.stringify(e) +
                    ")",
                  allFrames: !1,
                },
                function (e) {
                  chrome.DynamicToolRunner({
                    withContent: e[0],
                    query: "tool=qr-code",
                  });
                }
              );
            }),
              (toolMap[e].menuConfig[1].onClick = function (e, t) {
                chrome.tabs.executeScript(
                  t.id,
                  {
                    code:
                      "(" +
                      function (e) {
                        try {
                          if ("function" == typeof window.qrcodeContentScript) {
                            let t = window.qrcodeContentScript();
                            if ("function" == typeof t.decode)
                              return t.decode(e.srcUrl), 1;
                          }
                        } catch (e) {
                          return 0;
                        }
                        return 0;
                      }.toString() +
                      ")(" +
                      JSON.stringify(e) +
                      ")",
                    allFrames: !1,
                  },
                  function (o) {
                    0 === o[0] &&
                      chrome.tabs.executeScript(
                        t.id,
                        {
                          code:
                            "(" +
                            function (e) {
                              var t;
                              return (
                                ((t = e.srcUrl),
                                new Promise((e) => {
                                  let o = new Image();
                                  o.setAttribute("crossOrigin", "Anonymous"),
                                    (o.src = t),
                                    (o.onload = function () {
                                      let t = this.naturalWidth,
                                        n = this.naturalHeight,
                                        r = document.createElement("canvas");
                                      (r.style.cssText =
                                        "position:absolute;top:-10000px;left:-10000px"),
                                        document.body.appendChild(r),
                                        r.setAttribute("id", "qr-canvas"),
                                        (r.height = n + 100),
                                        (r.width = t + 100);
                                      let l = r.getContext("2d");
                                      (l.fillStyle = "rgb(255,255,255)"),
                                        l.fillRect(0, 0, r.width, r.height),
                                        l.drawImage(
                                          o,
                                          0,
                                          0,
                                          t,
                                          n,
                                          50,
                                          50,
                                          t,
                                          n
                                        ),
                                        e(r.toDataURL());
                                    }),
                                    (o.onerror = function () {
                                      e(t);
                                    });
                                })).then((e) => {
                                  window.__TEMP_DATA_URL_FOR_QRDECODE_ = e;
                                }),
                                "__TEMP_DATA_URL_FOR_QRDECODE_"
                              );
                            }.toString() +
                            ")(" +
                            JSON.stringify(e) +
                            ")",
                          allFrames: !1,
                        },
                        function (o) {
                          let n = o[0],
                            r = -1,
                            l = 0,
                            a = function () {
                              l++,
                                (r = setInterval(function () {
                                  chrome.tabs.executeScript(
                                    t.id,
                                    {
                                      code:
                                        "(" +
                                        function (e) {
                                          return window[e];
                                        }.toString() +
                                        ")(" +
                                        JSON.stringify(n) +
                                        ")",
                                      allFrames: !1,
                                    },
                                    function (t) {
                                      null === t[0] && l <= 10
                                        ? a()
                                        : (clearInterval(r),
                                          chrome.DynamicToolRunner({
                                            withContent: t[0] || e.srcUrl,
                                            query: "tool=qr-code&mode=decode",
                                          }));
                                    }
                                  );
                                }, 200));
                            };
                          a();
                        }
                      );
                  }
                );
              });
            break;
          default:
            toolMap[e].menuConfig[0].onClick = function (t, o) {
              chrome.DynamicToolRunner({
                withContent: "image-base64" === e ? t.srcUrl : "",
                query: `tool=${e}`,
              });
            };
        }
      });
      try {
        let jsText = CodeCacheMgr.get();
        jsText && eval(jsText),
          Object.keys(RemoteAwesome.newTools).forEach((e) => {
            toolMap[e]
              ? ((toolMap[e].name =
                  RemoteAwesome.newTools[e].name || toolMap[e].name),
                (toolMap[e].tips =
                  RemoteAwesome.newTools[e].tips || toolMap[e].tips),
                (toolMap[e].menuConfig =
                  RemoteAwesome.newTools[e].menuConfig ||
                  toolMap[e].menuConfig),
                void 0 !== RemoteAwesome.newTools[e].contentScript &&
                  (toolMap[e].contentScript =
                    RemoteAwesome.newTools[e].contentScript),
                void 0 !== RemoteAwesome.newTools[e].contentScriptCss &&
                  (toolMap[e].contentScriptCss =
                    RemoteAwesome.newTools[e].contentScriptCss),
                void 0 !== RemoteAwesome.newTools[e].offloadForbid &&
                  (toolMap[e].offloadForbid =
                    RemoteAwesome.newTools[e].offloadForbid))
              : (toolMap[e] = RemoteAwesome.newTools[e]);
          }),
          RemoteAwesome.removedTools.forEach((e) => {
            delete toolMap[e];
          });
      } catch (e) {}
      try {
        const e = "DEV-TOOLS:MY-TOOLS";
        let t = JSON.parse(localStorage.getItem(e) || "{}", (e, t) =>
          String(t).indexOf("function") > -1 ? new Function(`return ${t}`)() : t
        );
        Object.keys(t).forEach((e) => {
          toolMap[e] = t[e];
        });
      } catch (e) {}
      if (declareOnly) return toolMap;
      {
        let e = Object.keys(toolMap),
          t = [];
        return (
          e.forEach((e) => {
            t = t.concat([detectInstall(e), detectInstall(e, !0)]);
          }),
          Promise.all(t).then((t) => {
            t.forEach((t, o) => {
              let n = e[Math.floor(o / 2)],
                r = o % 2 == 0 ? "installed" : "menu";
              (toolMap[n][r] = t),
                toolMap[n].hasOwnProperty("_devTool") &&
                  (toolMap[n][r] = toolMap[n][r] && toolMap[n]._enable);
            });
            let o = SortToolMgr.get();
            if (o && o.length) {
              let e = {};
              return (
                o.forEach((t) => {
                  e[t] = toolMap[t];
                }),
                Object.keys(toolMap).forEach((t) => {
                  e[t] || (e[t] = toolMap[t]);
                }),
                e
              );
            }
            return toolMap;
          })
        );
      }
    };
  return {
    StorageMgr: StorageMgr,
    detectInstall: detectInstall,
    install: install,
    offLoad: offLoad,
    getInstalledTools: getInstalledTools,
    menuMgr: menuMgr,
    checkUpgrade: checkUpgrade,
    getContentScript: getContentScript,
    getToolTpl: getToolTpl,
    gcLocalFiles: gcLocalFiles,
    getAllTools: getAllTools,
    makeStorageUnlimited: makeStorageUnlimited,
    SortToolMgr: SortToolMgr,
    CodeCacheMgr: CodeCacheMgr,
  };
})();
export default Awesome;
